package com.ecp.commons;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

import com.ecp.commons.annotation.Column;
import com.ecp.commons.annotation.Entity;
import com.ecp.commons.annotation.Transient;
import com.ecp.commons.annotation.VariantType;
import com.ecp.crm.bean.User;
import com.ecp.util.Util;

public class Tools {

	private static final String	CLASSPATH	= Tools.class.getResource("/").getPath().replaceAll("target/classes/", "src/main/java/");
	private static final String	SERIALCLASS	= "com\\ecp\\commons\\cache\\Module.java";
	private static Set<String>	importClass	= new LinkedHashSet<String>();
	private static Set<String>	classSet	= new HashSet<String>();

	public static void main(String[] args) {
		try {
			String s = Tools.class.getResource("/").getPath().replaceAll("target/classes/", "src/main/java/");
			System.out.println(s);
			List<File> javaFile = loadFiles("com");
			if (javaFile != null && javaFile.size() > 0) {
				castSerialBean(javaFile);
			}
		} catch (Exception e) {    

			e.printStackTrace();
		}
	}

	public static void castSerialBean(List<File> javas) {

		String cname, shortName, entityName, fieldName, packagePath = "";
		Class<?> clazz;
		boolean usedDate = false;
		StringBuilder clazzContent = new StringBuilder();
		StringBuilder mapContent = new StringBuilder();
		StringBuilder resetContent = new StringBuilder();
		StringBuilder getter = new StringBuilder();
		StringBuilder setter = new StringBuilder();
		StringBuilder staticField = new StringBuilder();

		for (File f : javas) {
			try {
				cname = f.getAbsolutePath().substring(CLASSPATH.replaceAll("^\\/", "").replace("/", "\\").length());
//				 System.out.println(cname.replaceAll("\\.java", "").replace("\\", "."));
				clazz = Class.forName(cname.replaceAll("\\.java", "").replace("\\", "."));
				if (!clazz.isAnnotationPresent(Entity.class)) {
					continue;
				}
				if (!clazz.equals(User.class)) {
					continue;
				}
				System.out.println(cname);

				//				if (!clazz.equals(BaseAttribute.class)) {
				//					continue;
				//				}

				shortName = f.getName().replaceAll("\\.java", "");
				entityName = clazz.getAnnotation(Entity.class).value();

				clazzContent.append("\t@Override\n");
				clazzContent.append("\tpublic void defineVariantSet() {\n");
				clazzContent.append("\t\tthis.entity = \"" + entityName + "\";\n");

				String line = "";
				BufferedReader br = new BufferedReader(new FileReader(f));
				while ((line = br.readLine()) != null) {

					if (line.contains("java.util.Date")) {
						usedDate = true;
					}

					if (line.startsWith("package")) {
						packagePath = line;
					}

					if ((line.contains("static") && line.endsWith(";")) && !line.contains("serialVersionUID")) {
						staticField.append(line).append("\n");
					}
				}
				br.close();

				if (usedDate) {
					importClass.add("java.util.Date");
				}

				importClass.add("com.ecp.commons.annotation.VariantType");
				importClass.add("com.ecp.commons.serialization.GenericBean");
				importClass.add("com.ecp.commons.serialization.Variant");

				Field[] fields = clazz.getDeclaredFields();
				if (fields != null && fields.length > 0) {
					for (int i = 0; i < fields.length; i++) {
						Column c = fields[i].getAnnotation(Column.class);
						fieldName = fields[i].getName();

						// static
						if (fields[i].toString().contains("static")) {
							continue;
						}

						mapContent.append("\t\tmap.put(\"" + fieldName + "\", new Variant()" + setOthers(fields[i])
								+ ");\n");
						//
						if (c != null) {
							resetContent.append("\t\tthis.set(\"" + fieldName + "\", \"" + c.value() + "\");\n");
						}
						// getter
						getter.append("\tpublic " + fields[i].getType().getSimpleName() + " "
								+ Util.findGetter(fields[i]) + "() {\n");
						getter.append("\t\treturn this.get"
								+ (fields[i].getType().getSimpleName().substring(0, 1).toUpperCase() + fields[i]
										.getType().getSimpleName().substring(1)).replaceAll("Integer", "Int") + "(\""
								+ fieldName + "\");\n");
						getter.append("\t}\n\n");
						// setter
						setter.append("\tpublic void " + Util.findSetter(fields[i]) + "("
								+ fields[i].getType().getSimpleName() + " " + fieldName + ") {\n");
						setter.append("\t\tthis.set(\"" + fieldName + "\", " + fieldName + ");\n");
						setter.append("\t}\n\n");

					}
				}

				clazzContent.append(mapContent).append("\t}\n\n");
				clazzContent.append("\t@Override\n");
				clazzContent.append("\tpublic void resetValues() {\n");
				clazzContent.append(resetContent).append("\t}\n\n");

				clazzContent.append(getter);
				clazzContent.append(setter);

				clazzContent.append("\t@Override\n");
				clazzContent.append("\tpublic int hashCode() {\n");
				clazzContent.append("\t\tfinal int prime = 31;\n");
				clazzContent.append("\t\tint result = 1;\n");
				clazzContent.append("\t\tresult = prime * result + getId();\n");
				clazzContent.append("\t\treturn result;\n");
				clazzContent.append("\t}\n\n");

				clazzContent.append("\t@Override\n");
				clazzContent.append("\tpublic boolean equals(Object obj) {\n");
				clazzContent.append("\t\tif (this == obj)\n");
				clazzContent.append("\t\t\treturn true;\n");
				clazzContent.append("\t\tif (obj == null)\n");
				clazzContent.append("\t\t\treturn false;\n");
				clazzContent.append("\t\tif (getClass() != obj.getClass())\n");
				clazzContent.append("\t\t\treturn false;\n");
				clazzContent.append("\t\t" + shortName + " other = (" + shortName + ") obj;\n");
				clazzContent.append("\t\tif (getId() != other.getId())\n");
				clazzContent.append("\t\t\treturn false;\n");
				clazzContent.append("\t\treturn true;\n");
				clazzContent.append("\t}\n");
				clazzContent.append("}");

				PrintWriter pw = new PrintWriter(f);

				pw.print(packagePath.toString() + "\n\n");

				for (String s : importClass) {
					pw.print("import " + s + ";\n");
				}

				pw.print("\n");

				pw.print("public class " + shortName + " extends GenericBean {\n");
				pw.print("\n");
				if (!staticField.toString().equals("")) {
					pw.print(staticField + "\n");
				}

				pw.print(clazzContent);
				pw.flush();
				pw.close();

			} catch (Exception e) {

				e.printStackTrace();
			}
		}
	}

	private static String setOthers(Field field) {
		String content = "";
		boolean serial = false;
		if (field.isAnnotationPresent(Column.class) || field.isAnnotationPresent(Transient.class)) {
			serial = true;
		}
		Column c = field.getAnnotation(Column.class);
		if (c != null) {
			if (c.type().equals(VariantType.STRING)) {
				content += ".setLength(" + c.length() + ")";
			} else {
				content += ".setType(VariantType." + c.type() + ")";
			}
			if (c != null && c.pk()) {
				content += ".setPk(true)";
			}
			if (c != null && !c.nullable()) {
				content += ".setNullable(false)";
			}
			if (!serial) {
				content += ".setPersistent(false).setSerialize(false)";
			}
		} else {
			content += ".setType(VariantType.STRING).setPersistent(false).setSerialize(false)";
		}

		return content;
	}

	public static String setVariantTypeAndLength(Field field) {
		Column c = field.getAnnotation(Column.class);
		return c.type().equals(VariantType.STRING) ? ".setLength(" + c.length() + ")" : ".setType("
				+ c.type().toString() + ")";
	}

	public static void rewriteBean(List<File> javas) {
		try {
			String cname, className, shortName;
			Class<?> clazz;
			StringBuilder toJSONMethod, parseJSONMethod;
			StringBuilder content = new StringBuilder();
			importClass.add("org.json.JSONObject");
			importClass.add("org.springframework.stereotype.Repository");

			for (File f : javas) {
				cname = f.getAbsolutePath().substring(CLASSPATH.replaceAll("^\\/", "").replace("/", "\\").length());
				clazz = Class.forName(cname.replaceAll("\\.java", "").replace("\\", "."));
				if (!clazz.isAnnotationPresent(Entity.class)) {
					continue;
				}
				toJSONMethod = new StringBuilder();
				parseJSONMethod = new StringBuilder();

				className = cname.replace("\\", ".").replaceAll("\\.java", "");
				shortName = f.getName().replaceAll("\\.java", "");

				importClass.add(className);
				classSet.add(shortName);

				Field[] fields = clazz.getDeclaredFields();
				if (fields != null && fields.length > 0) {
					for (int i = 0; i < fields.length; i++) {
						if (!fields[i].isAnnotationPresent(Column.class)
								&& !fields[i].isAnnotationPresent(Transient.class)) {
							continue;
						}

						// Serialize Object.
						toJSONMethod.append("\t\tbuilder.append(\",\\\"").append(fields[i].getName())
								.append("\\\":\\\"\")");
						if (!fields[i].getType().equals(int.class) && !fields[i].getType().equals(long.class)
								&& !fields[i].getType().equals(float.class) && !fields[i].getType().equals(byte.class)
								&& !fields[i].getType().equals(char.class) && !fields[i].getType().equals(short.class)
								&& !fields[i].getType().equals(double.class)) {
							toJSONMethod.append(";\n\t\tbuilder.append(obj.").append(Util.findGetter(fields[i]))
									.append("() == null ? \"\" : obj.").append(Util.findGetter(fields[i])).append("()");
						} else {
							toJSONMethod.append(".append(obj.").append(Util.findGetter(fields[i])).append("()");
						}
						toJSONMethod.append(")").append(".append(\"\\\"\");\n");

						// DeSerialize Object.
						if (fields[i].getType().equals(Date.class) || fields[i].getType().equals(Timestamp.class)) {
							parseJSONMethod.append("\t\t\tif (!jobj.getString(\"" + fields[i].getName()
									+ "\").equals(\"\")) {\n");
							parseJSONMethod.append("\t\t\t\tobj.").append(Util.findSetter(fields[i])).append("(")
									.append(parseType(fields[i])).append(");\n");
							parseJSONMethod.append("\t\t\t}\n");
						} else {
							parseJSONMethod.append("\t\t\tobj.").append(Util.findSetter(fields[i])).append("(")
									.append(parseType(fields[i])).append(");\n");
						}
					}
					toJSONMethod.insert(toJSONMethod.lastIndexOf(";"), ".append(\"}\")");
					toJSONMethod.insert(0, "\t\tStringBuilder builder = new StringBuilder();\n");
					toJSONMethod.insert(0, "\n\tprivate String toJSON(" + shortName + " obj) {\n");
					toJSONMethod.replace(toJSONMethod.indexOf(","), toJSONMethod.indexOf(",") + 1, "{");
					toJSONMethod.append("\t\treturn builder.toString();\n");
					toJSONMethod.append("\t}\n");
					//
					parseJSONMethod.insert(0, "\t\t\tJSONObject jobj = new JSONObject(json);\n");
					parseJSONMethod.insert(0, "\t\ttry {\n");
					parseJSONMethod.insert(0, "\t\t" + shortName + " obj = new "
							+ f.getName().replaceAll("\\.java", "") + "();\n");
					parseJSONMethod.insert(0, "\n\tprivate " + shortName + " parse" + shortName + "(String json) {\n");

					parseJSONMethod.append("\t\t} catch (Exception e) {\n");
					parseJSONMethod.append("\t\t\te.printStackTrace();\n");
					parseJSONMethod.append("\t\t}\n");
					parseJSONMethod.append("\t\treturn obj;\n");
					parseJSONMethod.append("\t}\n");
				}
				content.append(toJSONMethod).append(parseJSONMethod);
			}
			PrintWriter pw = new PrintWriter(new File(CLASSPATH + SERIALCLASS));
			pw.print("package com.ecp.commons.cache;\n\n");
			for (String s : importClass) {
				pw.print("import " + s + ";\n");
			}
			pw.print("\n@Repository(\"serialService\")\n");
			pw.print("public class SerialServiceImpl implements SerialService {\n\n");
			pw.print("\tprivate static SimpleDateFormat\tsdf\t= new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\");\n\n");

			// public String toJSON(Class<?> clazz, Object obj);
			pw.print("\t@Override\n");
			pw.print("\tpublic String toJSON(Class<?> clazz, Object obj) {\n");
			for (String s : classSet) {
				pw.print("\t\tif (clazz.equals(" + s + ".class)) {\n");
				pw.print("\t\t\treturn toJSON((" + s + ") obj);\n");
				pw.print("\t\t}\n");
			}
			pw.print("\t\treturn null;\n");
			pw.print("\t}\n\n");

			// public Object parseObject(Class<?> clazz, String json) {
			pw.print("\t@Override\n");
			pw.print("\tpublic Object parseObject(Class<?> clazz, String json) {\n");
			pw.print("\t\tif (json.contains(\"\\n\") || json.contains(\"\\r\")) {\n");
			pw.print("\t\t\tjson = json.replaceAll(\"\\r\", \"\\\\r\").replaceAll(\"\\n\", \"\\\\n\");\n");
			pw.print("\t\t}\n");
			for (String s : classSet) {
				pw.print("\t\tif (clazz.equals(" + s + ".class)) {\n");
				pw.print("\t\t\treturn parse" + s + "(json);\n");
				pw.print("\t\t}\n");
			}
			pw.print("\t\treturn null;\n");
			pw.print("\t}\n");

			pw.print(content);
			pw.print("}");
			pw.flush();
			pw.close();
		} catch (Exception e) {

			e.printStackTrace();
		}
	}

	private static List<File> loadFiles(String path) {
		String root = CLASSPATH;
		if (path != null) {
			root = CLASSPATH + path;
		}
		List<File> javas = new ArrayList<File>();
		javas.addAll(loopAddFile(root));
		return javas;
	}

	private static List<File> loopAddFile(String root) {
		List<File> javas = new ArrayList<File>();
		File f = new File(root);
		if (f.isDirectory()) {
			File[] files = f.listFiles();
			if (files != null && files.length > 0) {
				for (File sub : files) {
					javas.addAll(loopAddFile(sub.getAbsolutePath()));
				}
			}
		} else if (f.isFile() && f.getName().endsWith("java")) {
			javas.add(f);
		}
		return javas;
	}

	private static String parseType(Field field) {
		if (field.getType().equals(Integer.class) || field.getType().equals(int.class)) {
			return "jobj.getInt(\"" + field.getName() + "\")";
		} else if (field.getType().equals(Long.class) || field.getType().equals(long.class)) {
			return "jobj.getLong(\"" + field.getName() + "\")";
		} else if (field.getType().equals(Double.class) || field.getType().equals(double.class)) {
			return "jobj.getDouble(\"" + field.getName() + "\")";
		} else if (field.getType().equals(Timestamp.class)) {
			importClass.add("java.sql.Timestamp");
			return "Timestamp.valueOf(jobj.getString(\"" + field.getName() + "\"))";
		} else if (field.getType().equals(Date.class)) {
			importClass.add("java.text.SimpleDateFormat");
			return "sdf.parse(jobj.getString(\"" + field.getName() + "\"))";
		} else if (field.getType().equals(String.class)) {
			return "jobj.getString(\"" + field.getName() + "\")";
		}
		return "jobj.getString(\"" + field.getName() + "\")";
	}
}
